package bingo.spring.context;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import net.bingosoft.common.ObjectRegistry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;

import bingo.spring.factory.AutowireBeanFactory;
/**
 * 2008-5-4 上午09:48:57
 * Author:yangdongyu(yangdy@bingosoft.net)
 * ReadMe:避免spring配置文件重新加载的问题
 * ChangeLog:
 * 实现spring配置文件的动态加载
 */
public class ContextLoaderListener implements ServletContextListener {
	private static final Log log = LogFactory.getLog(ContextLoaderListener.class);
	private static final org.springframework.web.context.ContextLoaderListener listener = new org.springframework.web.context.ContextLoaderListener(){
		@Override
		protected ContextLoader createContextLoader() {
			return new ContextLoader(){
				/**
				 * Instantiate the root WebApplicationContext for this loader, either the
				 * default context class or a custom context class if specified.
				 * <p>This implementation expects custom contexts to implement
				 * ConfigurableWebApplicationContext. Can be overridden in subclasses.
				 * @param servletContext current servlet context
				 * @param parent the parent ApplicationContext to use, or <code>null</code> if none
				 * @return the root WebApplicationContext
				 * @throws BeansException if the context couldn't be initialized
				 * @see ConfigurableWebApplicationContext
				 */
				protected WebApplicationContext createWebApplicationContext(
						ServletContext servletContext, ApplicationContext parent) throws BeansException {

					Class contextClass = determineContextClass(servletContext);
					if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
						throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
								"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
					}
					ConfigurableWebApplicationContext wac = null;
					final String pkgNames = servletContext.getInitParameter("lookup-pkgs");
					if(contextClass == XmlWebApplicationContext.class){
						wac = new XmlWebApplicationContext(){
							@Override
							protected void loadBeanDefinitions(
									XmlBeanDefinitionReader reader)
							throws BeansException, IOException {
								super.loadBeanDefinitions(reader);
								if(pkgNames!=null){
									//加载通过注解声明的Bean
									XmlBeanFactory autoFactory = new AutowireBeanFactory(pkgNames,null).getXmlBeanFactory();
									String []beanNames = autoFactory.getBeanDefinitionNames();
									for(String beanName : beanNames){
										//配置文件中存在就不用重新加载
										if(!reader.getBeanFactory().containsBeanDefinition(beanName)){
											reader.getBeanFactory().registerBeanDefinition(beanName, autoFactory.getBeanDefinition(beanName));
										}
									}
								}
							}
						};
					}else{
						wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
					}
					wac.setParent(parent);
					wac.setServletContext(servletContext);
					String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
					if (configLocation != null) {
						wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
								ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
					}

					wac.refresh();
					return wac;
				}

			};
		}

	};
	private static ServletContext sc;
	public void contextDestroyed(ServletContextEvent event) {
		listener.contextDestroyed(event);
		sc = null;
	}

	public void contextInitialized(ServletContextEvent event) {
		listener.contextInitialized(event);
		sc = event.getServletContext();
		ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
		ObjectRegistry.setOverrideFactory(ac);
	}

	public static void reloadContext() {
		log.debug("starting reload spring context ...");
		sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
		listener.contextInitialized(new ServletContextEvent(sc));
		ObjectRegistry.setOverrideFactory(WebApplicationContextUtils.getWebApplicationContext(sc));
		log.debug("reload spring context is done ...");
	}

}
